IndexedDB 与 Cache API
二者均为 异步、同源隔离 的持久化能力:IndexedDB 适合大量结构化业务数据;Cache Storage(Cache / CacheStorage)专门缓存 Request/Response,常与 Service Worker 组合做离线资源策略。
IndexedDB(NoSQL)
浏览器内置的键值型存储(异步 API),事务、object store、索引与游标可做复杂查询。
- 异步 API · 同源隔离 · 持久化 · 容量远大于 Web Storage · 可存结构化对象(非仅限字符串)
工作流程
shell
IndexedDB 通过 open 打开数据库;版本变更时触发 onupgradeneeded,在其中创建对象仓库与索引。
打开成功后所有读写必须通过事务,在 object store 上执行增删改查;结束事务后可关闭连接。
# 流程简图
open(库名, 版本)
↓
onupgradeneeded(建表、索引)
↓
onsuccess(得到 db)
↓
db.transaction(表, 模式) → objectStore
↓
add / get / put / delete / cursor
↓
success / error 回调
↓
事务完成,可 close
# 与关系型概念类比(仅帮助理解)
数据库 → 库
ObjectStore → 表
keyPath → 主键
index → 索引
transaction → 事务
cursor → 遍历操作示例
js
function actionUser(user) {
const request = indexedDB.open('myDB', 1);
request.onupgradeneeded = (e) => {
const db = e.target.result;
if (!db.objectStoreNames.contains('user')) {
const store = db.createObjectStore('user', { keyPath: 'id' });
store.createIndex('nameIdx', 'name', { unique: false });
store.createIndex('ageIdx', 'age', { unique: false });
}
};
request.onerror = (e) => {
console.log('打开失败', e.target.error);
};
request.onsuccess = (e) => {
const db = e.target.result;
const tx = db.transaction('user', 'readwrite');
const store = tx.objectStore('user');
const addReq = store.add(user);
addReq.onsuccess = () => console.log('添加成功');
addReq.onerror = () => console.log('添加失败');
const getReq = store.get(id);
getReq.onsuccess = (e) => {
console.log('查询结果:', e.target.result);
};
const reqAll = store.getAll();
reqAll.onsuccess = (e) => {
console.log('所有用户:', e.target.result);
};
const index = store.index('nameIdx');
const reqName = index.get(name);
reqName.onsuccess = (e) => {
console.log('按姓名查询:', e.target.result);
};
store.put(user);
store.delete(id);
tx.oncomplete = () => db.close();
};
}Cache API(Cache Storage)
CacheStorage(全局 caches)与 Cache 可在 窗口 或 Service Worker 中使用。典型是在 SW 的 fetch 中 match / put 做缓存优先、网络回退等。页面线程也可 caches.open 做预缓存,但需与 SW 安装/激活与「按版本清理」策略一致,避免旧缓存长期残留。
- 只能存成对的
Request/Response(多由fetch得到) - 全程 Promise,持久化、同源
- 一般与 Service Worker 搭配,而不是当「通用键值库」
流程与常见方法
shell
caches.open(缓存名) → 得到 cache 实例
→ add / addAll / put 写入
→ match / matchAll 查询
→ delete 或 caches.delete(名) 清理js
caches.open('my-cache-v1').then((cache) => {
cache.add('/api/user');
cache.add('/static/img/logo.png');
});
// cache.add(url) 相当于 fetch 后 cache.put(request, response)策略片段
js
// 缓存优先
async function getCacheFirst(url) {
const cache = await caches.open('my-cache-v1');
const res = await cache.match(url);
if (res) return res;
return fetch(url);
}
// 网络优先
async function getNetworkFirst(url) {
try {
const res = await fetch(url);
const cache = await caches.open('my-cache-v1');
cache.put(url, res.clone());
return res;
} catch {
return caches.match(url);
}
}
// 先用缓存、后台更新
async function staleWhileRevalidate(url) {
const cache = await caches.open('my-cache-v1');
const cachedRes = await cache.match(url);
fetch(url).then((res) => {
cache.put(url, res.clone());
});
return cachedRes || fetch(url);
}四个记忆点
text
只存 Request/Response
全部 Promise
持久、同源
与 SW 场景最搭,别和 IndexedDB 混用角色